package controllers

import command.CommandUtils

import java.io.File
import java.nio.file.Files
import javax.inject.Inject
import org.apache.commons.io.FileUtils
import play.api.mvc.{AbstractController, ControllerComponents}
import tool.{FileTool, _}
import utils.Utils
import models.Tables._
import org.joda.time.{DateTime, Days}
import dao._
import org.apache.commons.lang3.StringUtils
import play.api.libs.json.{JsObject, Json}

import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import implicits.Implicits._
import spdf._

import java.sql.Date
import java.util.Calendar

/**
 * Created by yz on 2019/1/3
 */
class PredictController @Inject()(cc: ControllerComponents, formTool: FormTool, tool: Tool,
                                  missionDao: MissionDao, extraDataDao: ExtraDataDao,
                                  trashDao: TrashDao) extends AbstractController(cc) {

  def predictBefore = Action { implicit request =>
    Ok(views.html.user.predict())
  }

  def predict = Action.async { implicit request =>
    val data = formTool.predictForm.bindFromRequest().get
    val userId = Tool.getUserId
    val myMessage = Tool.dataCheck(data).toMyMessage
    if (myMessage.valid) {
      val time = new Date(System.currentTimeMillis())
      val row = MissionRow(0, userId, data.sampleId, data.name, data.age, data.bmi, data.fbg, data.ggt, data.hdlch,
        data.tbil, data.albumin, data.ast, data.plt, time)
      missionDao.selectOp(row).flatMap { missionOp =>
        missionOp.map { mission =>
          val newRow = row.copy(id = mission.id)
          missionDao.update(newRow).map { x =>
            mission.id
          }
        }.getOrElse {
          missionDao.insertAndReturnId(row)
        }
      }.flatMap { missionId =>
        val extraRow = tool.getEmptyExtraDataRow(missionId, userId).copy(sampleId = data.sampleId, name = data.name,
          checkDate = Utils.dataTime2String(new DateTime()))
        val trashRow = TrashRow(missionId, userId, true)
        extraDataDao.insertOrUpdate(extraRow).zip(trashDao.insertOrUpdate(trashRow)).map { x =>
          val json = Json.obj("missionId" -> missionId)
          Ok(json)
        }
      }
    } else {
      Utils.result2Future(Ok(Json.obj("valid" -> myMessage.valid, "message" -> myMessage.message)))
    }
  }

  def export = Action.async { implicit request =>
    val userId = Tool.getUserId
    val data = formTool.basicInfoForm.bindFromRequest().get
    val pdfData = formTool.pdfDataForm.bindFromRequest().get
    val missionId = FormTool.missionIdForm.bindFromRequest().get.missionId
    val tmpDir = tool.getUserIdDir(userId)
    val outPdfFile = new File(tmpDir, "live_fat_out.pdf")
    val pdf = Pdf(s"${tool.binDir}/wkhtmltopdf/bin/wkhtmltopdf.exe", new PdfConfig {
      orientation := Portrait
      pageSize := "A4"
      pageSize := "Letter"
      marginTop := "0.5in"
      marginBottom := "0in"
      marginLeft := "0.5in"
      marginRight := "0.5in"
      title := "LiveFat PC"
      printMediaType := Some(true)
    })
    missionDao.selectByMissionId(missionId).flatMap { row =>
      val missionIdDir = tool.getMissionIdDirById(missionId)
      val outFile = new File(missionIdDir, "out.txt")
      val logoFile = new File(tool.dataDir, "logo.png")
      val logoBase64 = logoFile.base64
      val qrBase64 = new File(tool.dataDir, "qrcode.jpg").base64
      val imageMap = Map("image0" -> pdfData.base640, "image1" -> pdfData.base641, "image2" -> pdfData.base642,
        "image3" -> pdfData.base643, "image4" -> pdfData.base644, "image5" -> pdfData.base645, "image6" -> pdfData.base646,
        "image7" -> pdfData.base647, "image8" -> pdfData.base648)
      val imageInfo = imageMap ++ Map("logo" -> logoBase64, "qrCode" -> qrBase64)
      val htmlFile = new File(tmpDir, "out.html")
      val str = views.html.user.html(data, row, imageInfo, pdfData).toString()
      FileUtils.writeStringToFile(htmlFile, str)
      pdf.run(views.html.user.html(data, row, imageInfo, pdfData).toString(), outPdfFile)
      val bytes = FileUtils.readFileToByteArray(outPdfFile)
      val extraDataRow = ExtraDataRow(row.id, row.userId, data.sample, data.unit, data.address, data.name, data.sex,
        data.office, data.doctor, data.number, data.sampleTime, data.submitTime, data.sampleType
        , data.sampleStatus, data.title, data.danger, data.reporter, data.checker, data.checkDate, data.reportDate
      )
      extraDataDao.insertOrUpdate(extraDataRow).map { x =>
        Redirect(s"${routes.PredictController.exportPdf}?dir=${tmpDir.getAbsolutePath.urlEncode}")
      }
    }
  }

  def exportPdf = Action { implicit request =>
    val data = FormTool.dirForm.bindFromRequest().get
    val tmpDir = new File(data.dir)
    val outPdfFile = new File(tmpDir, "live_fat_out.pdf")
    val bytes = FileUtils.readFileToByteArray(outPdfFile)
    Ok(bytes).as("application/pdf")
  }

  //  def export = Action.async { implicit request =>
  //    val data = formTool.basicInfoForm.bindFromRequest().get
  //    val pdfData = formTool.pdfDataForm.bindFromRequest().get
  //    val missionId = FormTool.missionIdForm.bindFromRequest().get.missionId
  //    val tmpDir = tool.createTempDirectory("tmpDir")
  //    val outPdfFile = new File(tmpDir, "live_fat_out.pdf")
  //    val pdf = Pdf(s"${tool.binDir}/wkhtmltopdf/bin/wkhtmltopdf.exe", new PdfConfig {
  //      orientation := Portrait
  //      pageSize := "A4"
  //      pageSize := "Letter"
  //      marginTop := "0.5in"
  //      marginBottom := "0in"
  //      marginLeft := "0.5in"
  //      marginRight := "0.5in"
  //    })
  //    missionDao.selectByMissionId(missionId).flatMap { row =>
  //      val missionIdDir = tool.getMissionIdDirById(missionId)
  //      val outFile = new File(missionIdDir, "out.txt")
  //      val logoFile = new File(tool.dataDir, "logo_huiyun.png")
  //      val logoBase64 = logoFile.base64
  //      val qrBase64 = new File(tool.dataDir, "qr_code_huiyun.jpg").base64
  //      val imageInfo = Map("myImage" -> pdfData.svgStr, "logo" -> logoBase64, "qrCode" -> qrBase64)
  //      val htmlFile = new File(tmpDir, "out.html")
  //      val extraDataRow = ExtraDataRow(row.id, row.userId, data.sample, data.unit, data.address, data.name, data.sex,
  //        data.office, data.doctor, data.number, data.sampleTime, data.submitTime, data.sampleType
  //        , data.sampleStatus, data.title, data.danger, data.reporter, data.checker, data.checkDate, data.reportDate
  //      )
  //      extraDataDao.insertOrUpdate(extraDataRow).map { x =>
  //        tool.deleteDirectory(tmpDir)
  //        Ok(views.html.user.html(data, row, imageInfo, pdfData))
  //      }
  //    }
  //  }

  def batchPredictBefore = Action { implicit request =>
    Ok(views.html.user.batchPredict())
  }

  def downloadExampleFile = Action { implicit request =>
    val data = formTool.fileNameForm.bindFromRequest().get
    val file = new File(tool.path, s"example/${data.fileName}")
    Ok.sendFile(file).withHeaders(
      CACHE_CONTROL -> "max-age=3600",
      CONTENT_DISPOSITION -> s"attachment; filename=${
        file.getName
      }",
      CONTENT_TYPE -> "application/x-download"
    )
  }

  def batchPredict = Action.async(parse.multipartFormData) { implicit request =>
    val tmpDir = tool.createTempDirectory("tmpDir")
    val inputFile = new File(tmpDir, "input.xlsx")
    val file = request.body.file("file").get
    file.ref.moveTo(inputFile, replace = true)
    val myMessage = FileTool.fileCheck(inputFile)
    if (myMessage.valid) {
      val userId = Tool.getUserId
      val maps = inputFile.xlsxLines().lineMap
      tool.deleteDirectory(tmpDir)
      val time = new Date(System.currentTimeMillis())
      val ts = maps.map { case map =>
        //        val randomInt = (math.random() * 365).toInt
        //        val randomDate = (new DateTime()).plusDays(randomInt)
        //        val time = new Date(randomDate.getMillis)
        val orinalData = tool.getOrinalData(map)
        val missionRow = MissionRow(0, userId, orinalData.sampleId, "", orinalData.age, orinalData.bmi, orinalData.fbg, orinalData.ggt,
          orinalData.hdlch, orinalData.tbil, orinalData.albumin, orinalData.ast, orinalData.plt, time)
        val extraData = tool.getExtraData(map)
        val checkDate = if (StringUtils.isEmpty(extraData.checkDate)) Utils.dataTime2String(new DateTime()) else extraData.checkDate
        val extraRow = tool.getEmptyExtraDataRow(0, userId).copy(sampleId = orinalData.sampleId, name = extraData.name,
          unit = extraData.unit, address = extraData.address, sex = extraData.sex, office = extraData.office,
          doctor = extraData.doctor, number = extraData.number, sampleTime = extraData.sampleTime, submitTime = extraData.submitTime,
          sampleType = extraData.sampleType, sampleStatus = extraData.sampleStatus,
          title = extraData.title, reporter = extraData.reporter, checker = extraData.checker, checkDate = checkDate,
          reportDate = extraData.reportDate
        )
        val trashRow = TrashRow(0, userId, true)
        (missionRow, extraRow, trashRow)
      }.distinctByKeepLast(x => (x._1.sampleId, x._1.endTime.toString))
      missionDao.selectAll(userId).flatMap { dbMissions =>
        val dbMap = dbMissions.map { x =>
          ((x.sampleId, x.endTime.toString) -> x)
        }.toMap
        val newTs = ts.map { x =>
          dbMap.get((x._1.sampleId, x._1.endTime.toString)).map { dbMission =>
            val newMissionRow = x._1.copy(id = dbMission.id)
            val newExtraRow = x._2.copy(id = dbMission.id)
            val newTrashRow = x._3.copy(id = dbMission.id)
            (newMissionRow, newExtraRow, newTrashRow)
          }.getOrElse {
            x
          }
        }
        missionDao.insertOrUpdates(newTs).flatMap { missionIds =>
          missionDao.selectByMissionIds(missionIds).zip(extraDataDao.selectByMissionIds(missionIds)).map { case (x, extraRows) =>
            val missionRows = x
            val extraMap = extraRows.map(x => (x.id, x)).toMap
            val t2s = missionRows.map(x => (x, extraMap.getOrElse(x.id, tool.getEmptyExtraDataRow(x.id, x.userId))))
            val array = Utils.getArrayByT2s(t2s)
            Ok(Json.obj("array" -> array, "valid" -> true))
          }
        }
      }
    } else {
      tool.deleteDirectory(tmpDir)
      Utils.result2Future(Ok(Json.obj("valid" -> myMessage.valid, "message" -> myMessage.message)))
    }
  }

  def predictResult = Action.async { implicit request =>
    val data = FormTool.missionIdForm.bindFromRequest().get
    missionDao.selectByMissionId(data.missionId).zip(extraDataDao.selectByMissionId(data.missionId)).map { case (row, extraRow) =>
      val missionIdDir = tool.getMissionIdDirById(data.missionId)
      val outFile = new File(missionIdDir, "out.txt")
      val extraDataJson = Utils.getJsonByT(extraRow)
      val missionJson = tool.getMissionJson(row)
      val json = Json.obj("missionId" -> data.missionId, "sampleId" -> row.sampleId,
        "name" -> row.name, "age" -> row.age, "extraData" -> extraDataJson, "mission" -> missionJson)
      Ok(json)
    }
  }

  def getLinearPlotData = Action.async { implicit request =>
    val data = FormTool.missionIdForm.bindFromRequest().get
    val dateData = FormTool.dateForm.bindFromRequest().get
    missionDao.selectByMissionId(data.missionId).flatMap { curMission =>
      missionDao.selectAll(curMission.sampleId, curMission.userId).map { tmpAllMissions =>
        val allMissions = tmpAllMissions.filter { x =>
          val curDate = new DateTime(x.endTime.getTime)
          val startB = dateData.startDate.flatMap { date =>
            Utils.string2DateTimeOp(date, "yyyy-MM-dd")
          }.map { dateTime =>
            dateTime.isBefore(curDate) || dateTime.isEqual(curDate)
          }.getOrElse(true)
          val endB = dateData.endDate.flatMap { date =>
            Utils.string2DateTimeOp(date, "yyyy-MM-dd")
          }.map { dateTime =>
            dateTime.isAfter(curDate) || dateTime.isEqual(curDate)
          }.getOrElse(true)
          startB && endB
        }
        val index = allMissions.indexOf(curMission)
        val dates = allMissions.map(_.endTime.toString)
        val ages = allMissions.map(_.age)
        val bmis = allMissions.map(_.bmi)
        val fbgs = allMissions.map(_.fbg)
        val ggts = allMissions.map(_.ggt)
        val hdlchs = allMissions.map(_.hdlch)
        val tbils = allMissions.map(_.tbil)
        val albumins = allMissions.map(_.albumin)
        val asts = allMissions.map(_.ast)
        val plts = allMissions.map(_.plt)
        val dTickN = 5
        val oneDay = 86400000L
        val dTick = if (allMissions.nonEmpty) {
          val startDate = new DateTime(allMissions.last.endTime.getTime)
          val endDate = new DateTime(allMissions.head.endTime.getTime)
          val dateRange = Days.daysBetween(startDate, endDate)
          math.max(dateRange.getDays / dTickN, 1) * oneDay
        } else {
          oneDay
        }
        val ageDTick = if (allMissions.nonEmpty) {
          val agesInt = ages.map(_.toInt)
          val range = agesInt.max - agesInt.min
          s"${math.max((range + 1) / 4, 1)}"
        } else {
          "1"
        }
        val finalDTick = dTick match {
          case x if x > oneDay * 365 =>
            val year = x / (oneDay * 365)
            s"Y${year}"
          case x if x > oneDay * 30 =>
            val month = x / (oneDay * 30)
            s"M${month}"
          case x => x.toString
        }
        val colors = allMissions.indices.map { x =>
          if (x == index) "red" else "#1F77B4"
        }
        val array = List(
          Json.obj("x" -> dates, "y" -> ages, "title" -> "年龄-时间变化曲线", "yTitle" -> "岁"),
          Json.obj("x" -> dates, "y" -> bmis, "title" -> "BMI-时间变化曲线", "yTitle" -> "kg/m<sup>2</sup>"),
          Json.obj("x" -> dates, "y" -> fbgs, "title" -> "FBG浓度-时间变化曲线", "yTitle" -> "mmol/L"),
          Json.obj("x" -> dates, "y" -> ggts, "title" -> "GGT浓度-时间变化曲线", "yTitle" -> "U/L"),
          Json.obj("x" -> dates, "y" -> hdlchs, "title" -> "HDLch浓度-时间变化曲线", "yTitle" -> "mmol/L"),
          Json.obj("x" -> dates, "y" -> tbils, "title" -> "TBil浓度-时间变化曲线", "yTitle" -> "μmol/L"),
          Json.obj("x" -> dates, "y" -> albumins, "title" -> "ALB浓度-时间变化曲线", "yTitle" -> "g/L"),
          Json.obj("x" -> dates, "y" -> asts, "title" -> "AST浓度-时间变化曲线", "yTitle" -> "U/L"),
          Json.obj("x" -> dates, "y" -> plts, "title" -> "PLT浓度-时间变化曲线", "yTitle" -> "10^9/L"),
        )
        val json = Json.obj("array" -> Json.toJson(array), "dTick" -> finalDTick, "colors" -> colors,
          "ageDTick" -> ageDTick)
        Ok(json)
      }
    }
  }


}
